{
  // 实现一个功能，发送请求5s后，如果没有数据返回，中断请求，提示错误
  const request = (params) => {
    const otps = {
      timeout: 50000,
      ...params
    }

    return new Promise((resolve, reject) => {
      let isTimeOut = false;
      let xhr = new XMLHttpRequest()
      let timer = setTimeout(() => {
        isTimeOut = true
        xhr.abort()
        reject('request is timeout !!!')
      }, otps.timeout)

      xhr.open('GET', opts.url)
      xhr.send(null)
      xhr.onreadystatechange = () => {
        if (xhr.readyState === 4) {
          if (isTimeOut) return // 忽略终止请求
          clearTimeout(timer) // 取消等待的超时
          if (xhr.status === 200) {
            resolve(xhr.responseText)
          } else {
            reject('request is fail !!!')
          }
        }
      }
    })
  }

  const getData = async () => {
    const opts = {
      url: 'xxx'
    }

    try {
      const res = await request(otps)
    } catch (error) {
      console.log('error: ', error);
    }
  }

  getData()
}

{
  // 写一个函数，输出多级嵌套结构object的所有key值
  let obj = {
    a: '12',
    b: '34',
    first: {
      c: '12',
      d: '23',
      second: {
        f: '323'
      }
    }
  }
  function getAllKeys(o) {
    if (typeof o !== 'object') return
    let keys = []
    for (const key in o) {
      // 判断对象切不是数组
      if (o[key] instanceof Object && !Array.isArray(o[key])) {
        keys = keys.concat(getAllKeys(o[key]))
      } else {
        keys.push(key)
      }
    }
    return keys
  }
}

{
  // 给定一个数组，找到每个元素右侧第一个比它大的数字，没有返回-1，最后返回一个数组
  // 如：[2,6,3,8,10,9] => [6,8,8,10,-1,-1]

  // 暴力双循环
  function findMaxRight(arr) {
    let res = []
    for (let i = 0; i < arr.length; i++) {
      for (let j = i + 1; j < arr.length; j++) {
        if (arr[j] > arr[i]) {
          res[i] = arr[j]
          break
        } else {
          res[i] = -1
        }
      }
    }
    res[arr.length - 1] = -1
    return res
  }
}

{
  // js统计页面所有已经s h开头的标签，并计算标签种类
  let el = Array.from(document.getElementsByTagName('*'));
  let reg = /^[h|s].+/gi;
  let obj = {};
  el.map(item => {
    const tagName = item.tagName;
    if (reg.test(tagName)) {
      !obj[tagName] ? obj[tagName] = 1 : obj[tagName]++
    }
  });
  console.log(obj);
}

{
  /**
 * 版本比较
 * @function version_compare
 * @param {string} version1 - 比较的版本1
 * @param {string} version2 - 比较的版本2
 * @return {number} 返回比较结果：0，-1，1
 */
  export default (version1, version2) => {
    version1 = toString(version1, '');
    version2 = toString(version2, '');
    let a = version1.split('.');
    let b = version2.split('.');
    let i = 0;
    let len = Math.max(a.length, b.length);
    for (; i < len; i++) {
      if ((a[i] && !b[i] && parseInt(a[i], 10) > 0) || (parseInt(a[i], 10) > parseInt(b[i], 10))) {
        return 1;
      }
      else if ((b[i] && !a[i] && parseInt(b[i], 10) > 0) || (parseInt(a[i], 10) < parseInt(b[i], 10))) {
        return -1;
      }

    }
    return 0;
  };
}

{
  // version是一个项目版本号列表，因多人维护不规则，实现一个版本号处理函数
  // 从小到大排序 注意 '1.45.0' 比 '1.5' 大
  function sortVsrsion(versions) {
    return versions.sort((a, b) => {
      let aa = a.split('.');
      let bb = b.split('.');
      let len = aa.length > bb.length ? aa.length : bb.length;
      for (let i = 0; i < len; i++) {
        let x = aa[i] || 0;
        let y = bb[i] || 0;
        if (x !== y) {
          return x - y
        }
      }
    })
  }
  let versions = ['1.45.0', '1.5', '6', '3.3.3.3.3.3'];
  console.log(sortVsrsion(versions)); // ['1.5', '1.45.0', '3.3.3.3.3.3', '6']
}

{
  // 倒计时（天 时 分 秒）
  const minuteSecond = 60;
  const hourSecond = minuteSecond * 60;
  const daySecond = hourSecond * 24;

  getCountDownTimeStr(timestamp = 0) {
    if (timestamp === null) {
      return '---';
    }

    if (timestamp < 0) {
      timestamp = 0;
    }

    let timeArray = [{
      unit: '天',
      secondCount: daySecond
    }, {
      unit: '时',
      secondCount: hourSecond
    }, {
      unit: '分',
      secondCount: minuteSecond
    }, {
      unit: '秒',
      secondCount: 1
    }];

    let time = Math.ceil(timestamp / 1000);
    let resultArray = timeArray.map(timeItem => {
      let count = Math.floor(time / timeItem.secondCount);
      time -= count * timeItem.secondCount;
      return {
        count: count.toString().length < 2 ? '0' + count : count,
        ...timeItem
      };
    });

    return resultArray.reduce((pre, result) => {
      if (pre === '') {
        if (result.count > 0 || result.unit === '秒') {
          return [<span className={style.countDownTime}>{result.count}</span>,
          <span className={style.countDownUnit}>{result.unit}</span>];
        } else {
          return pre;
        }
      } else {
        return pre.concat([
          [<span className={style.countDownTime}>{result.count}</span>,
          <span className={style.countDownUnit}>{result.unit}</span>]
        ]);
      }
    }, '');
  }
}

{
  // 判断p标签内是否有内容
  // 方法一
  const arr = '<p>Hello</p>'.split('</');
  console.log(arr[1].split('</'));
  // 方法二
  console.log('<p>Hello</p>'.replace(/<(?!img|embed).*?>/ig, ''));
  // 方法三
  /<p.*>(\s)*.+(\s)*<\/p>/.test('<p>Hello</p>')
}

{
  // 在输入框中如何判断输入的是一个正确的网址。
  function isUrl(url) {
    try {
      let { href, origin, protocol, host, hostname } = new URL(url)
      console.log(href)
      console.log(origin)
      console.log(protocol)
      console.log(host)
      console.log(hostname)
      return true
    } catch (e) {
      return false
    }
  }
  console.log(isUrl('http://www.baidu.com'))
}

{
  // let m = {
  // 	1: {
  //     1: '小赚一笔', // X<1万
  //     10: '轻松过万', // 1万<=X<10万（
  //     100: '小康之家', // 10万<=X<100万
  //     1000: '百万富翁', // 100万<=X<1000万（
  //     10000: '千万富豪', // 1000万<=X<1亿
  //     100000: '资产过亿', // 1亿<=X
  //   },
  // }

  const titleList = ['小赚一笔', '轻松过万', '小康之家', '百万富翁', '千万富豪', '资产过亿'];

  function getTitle(num, list = titleList) {
    let count = 0;
    while (Math.floor(num / 10) > 0) {
      count++;
      num = Math.floor(num / 10);
    }
    return list[count] || list[list.length - 1];
  }

  console.log(getTitle(0));
  console.log(getTitle(9));
  console.log(getTitle(99));
  console.log(getTitle(999));
}

{
  // 封装数据类型函数
  const type = (() => {
    const type = Object.create(null);
    const typeAry = ['String', 'Number', 'Object', 'Array', 'Null', 'Boolean', 'Undefined'];
    for (let i = 0; i < typeAry.length; i++) {
      type[`is${typeAry[i]}`] = function (args) {
        return Object.prototype.toString.call(args) === `[object ${typeAry[i]}]`;
      };
    }
    return type;
  })()

  console.log(type.isArray({ a: 12, length: 1 }));
  console.log(type.isString(12));
}

{
  // 自记忆函数封装
  // 分函数，不同参数取值
  function memory(fn) {
    const cache = {};
    return function (...args) {
      const key = JSON.stringify(args);
      if (cache[key]) return cache[key];
      cache[key] = fn.call(fn, ...args);
    };
  }
}

{
  // 检验URL链接是否有效
  function getUrlState(URL) {
    var xmlhttp = new ActiveXObject('microsoft.xmlhttp');
    xmlhttp.Open('GET', URL, false);
    try {
      xmlhttp.Send();
    } catch (e) {
    } finally {
      var result = xmlhttp.responseText;
      if (result) {
        if (xmlhttp.Status == 200) {
          return true;
        } else {
          return false;
        }
      } else {
        return false;
      }
    }
  }
}

{
  // 完美判断是否为网址
  function IsURL(strUrl) {
    var regular = /^\b(((https?|ftp):\/\/)?[-a-z0-9]+(\.[-a-z0-9]+)*\.(?:com|edu|gov|int|mil|net|org|biz|info|name|museum|asia|coop|aero|[a-z][a-z]|((25[0-5])|(2[0-4]\d)|(1\d\d)|([1-9]\d)|\d))\b(\/[-a-z0-9_:\@&?=+,.!\/~%\$]*)?)$/i;
    if (regular.test(strUrl)) {
      return true;
    } else {
      return false;
    }
  }
}

{
  // 时间个性化输出功能

  /*
  1、< 60s, 显示为“刚刚”
  2、>= 1min && < 60 min, 显示与当前时间差“XX分钟前”
  3、>= 60min && < 1day, 显示与当前时间差“今天 XX:XX”
  4、>= 1day && < 1year, 显示日期“XX月XX日 XX:XX”
  5、>= 1year, 显示具体日期“XXXX年XX月XX日 XX:XX”
  */
  function timeFormat(time) {
    var date = new Date(time),
      curDate = new Date(),
      year = date.getFullYear(),
      month = date.getMonth() + 1,
      day = date.getDate(),
      hour = date.getHours(),
      minute = date.getMinutes(),
      curYear = curDate.getFullYear(),
      curHour = curDate.getHours(),
      timeStr;

    if (year < curYear) {
      timeStr = year + '年' + month + '月' + day + '日 ' + hour + ':' + minute;
    } else {
      var pastTime = curDate - date,
        pastH = pastTime / 3600000;

      if (pastH > curHour) {
        timeStr = month + '月' + day + '日 ' + hour + ':' + minute;
      } else if (pastH >= 1) {
        timeStr = '今天 ' + hour + ':' + minute + '分';
      } else {
        var pastM = curDate.getMinutes() - minute;
        if (pastM > 1) {
          timeStr = pastM + '分钟前';
        } else {
          timeStr = '刚刚';
        }
      }
    }
    return timeStr;
  }
}

{
  // 常用的正则表达式
  //正整数
  /^[0-9]*[1-9][0-9]*$/;
  //负整数
  /^-[0-9]*[1-9][0-9]*$/;
  //正浮点数
  /^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$/;
  //负浮点数
  /^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$/;
  //浮点数
  /^(-?\d+)(\.\d+)?$/;
  //email地址
  /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/;
  //url地址
  // /^[a-zA-z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$/;
  //年/月/日（年-月-日、年.月.日）
  /^(19|20)\d\d[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$/;
  //匹配中文字符
  /[\u4e00-\u9fa5]/;
  //匹配帐号是否合法(字母开头，允许5-10字节，允许字母数字下划线)
  /^[a-zA-Z][a-zA-Z0-9_]{4,9}$/;
  //匹配空白行的正则表达式
  /\n\s*\r/;
  //匹配中国邮政编码
  /[1-9]\d{5}(?!\d)/;
  //匹配身份证
  /\d{15}|\d{18}/;
  // /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
  //匹配国内电话号码
  /(\d{3}-|\d{4}-)?(\d{8}|\d{7})?/;
  //匹配IP地址
  /((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)/;
  //匹配首尾空白字符的正则表达式
  /^\s*|\s*$/;
  //匹配HTML标记的正则表达式
  // < (\S*?)[^>]*>.*?|< .*? />;
  // 过滤Emoji：name.replace(/\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g, "")
  // 过滤Emoji：emoji.replace(/[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F]|[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF]|[0-9|*|#]\uFE0F\u20E3|[0-9|#]\u20E3|[\u203C-\u3299]\uFE0F\u200D|[\u203C-\u3299]\uFE0F|[\u2122-\u2B55]|\u303D|[\A9|\AE]\u3030|\uA9|\uAE|\u3030/ig, '');
  // let emojiReg = /(\ud83c[\udf00-\udfff])|(\ud83d[\udc00-\ude4f])|(\ud83d[\ude80-\udeff])/
  // 匹配中文字符的正则表达式： [\u4e00-\u9fa5] 
  // 匹配双字节字符（包括汉字在内）：[^\x00-\xff] 
  // 匹配空行的正则表达式：\n[\s| ]*\r 
  // 匹配 HTML 标记的正则表达式：<(.*)>.*<\/\1>|<(.*) \/>
  // 匹配首尾空格的正则表达式：(^\s*)|(\s*$) 
  // 匹配 IP 地址的正则表达式：/(\d+)\.(\d+)\.(\d+)\.(\d+)/g
  // 匹配 Email 地址的正则表达式：\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
  // 匹配网址 URL 的正则表达式：http://(/[\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?
  // sql 语句：^(select|drop|delete|create|update|insert).*$ 
  // 非负整数：^\d+$ 
  // 正整数：^[0-9]*[1-9][0-9]*$ 
  // 非正整数：^((-\d+)|(0+))$ 
  // 负整数：^-[0-9]*[1-9][0-9]*$ 
  // 整数：^-?\d+$ 
  // 非负浮点数：^\d+(\.\d+)?$ 
  // 正浮点数：^((0-9)+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$ 
  // 非正浮点数：^((-\d+\.\d+)?)|(0+(\.0+)?))$ 
  // 英文字符串：^[A-Za-z]+$ 
  // 英文大写串：^[A-Z]+$ 
  // 英文小写串：^[a-z]+$ 
  // 英文字符数字串：^[A-Za-z0-9]+$ 
  // 英数字加下划线串：^\w+$ 
  // E-mail地址：^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$ 
  // URL：^[a-zA-Z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\s*)?$ 或：^http:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&_~`@[\]\':+!]*([^<>\"\"])*$ 
  // 邮政编码：^[1-9]\d{5}$ 
  // 电话号码：^((\(\d{2,3}\))|(\d{3}\-))?(\(0\d{2,3}\)|0\d{2,3}-)?[1-9]\d{6,7}(\-\d{1,4})?$ 
  // 手机号码：^((\(\d{2,3}\))|(\d{3}\-))?13\d{9}$ 
  // 双字节字符（包括汉字在内）：^\x00-\xff 
  // 匹配首尾空格：(^\s*)|(\s*$)
  // 匹配 HTML 标记：<(.*)>.*<\/\1>|<(.*) \/> 
  // 匹配空行：\n[\s| ]*\r 
  // 提取信息中的网络链接：(h|H)(r|R)(e|E)(f|F) *= *('|")?(\w|\\|\/|\.)+('|"| *|>)? 
  // 提取信息中的邮件地址：\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)* 
  // 提取信息中的图片链接：(s|S)(r|R)(c|C) *= *('|")?(\w|\\|\/|\.)+('|"| *|>)? 
  // 提取信息中的 IP 地址：(\d+)\.(\d+)\.(\d+)\.(\d+) 
  // 提取信息中的中国手机号码：(86)*0*13\d{9} 
  // 提取信息中的中国固定电话号码：(\(\d{3,4}\)|\d{3,4}-|\s)?\d{8} 
  // 提取信息中的中国电话号码（包括移动和固定电话）：(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14} 
  // 提取信息中的中国邮政编码：[1-9]{1}(\d+){5} 
  // 提取信息中的浮点数（即小数）：(-?\d*)\.?\d+ 
  // 提取信息中的任何数字 ：(-?\d*)(\.\d+)? 
  // IP：(\d+)\.(\d+)\.(\d+)\.(\d+) 
  // 电话区号：^0\d{2,3}$
  // 腾讯 QQ 号：^[1-9]*[1-9][0-9]*$ 
  // 帐号（字母开头，允许 5-16 字节，允许字母数字下划线）：^[a-zA-Z][a-zA-Z0-9_]{4,15}$ 
  // 中文、英文、数字及下划线：^[\u4e00-\u9fa5_a-zA-Z0-9]+$
  // 中文、英文、数字、下划线以及特殊符号开头：/^[\u4e00-\u9fa5_a-zA-Z0-9_!@#￥%&]+$/
  // 不能以/**/ 开头和结尾的正则： /(^\/\*\*\/)|(\/\*\*\/$)/
  // let reg = /(^\/\*\*\/)|(\/\*\*\/$)/;
  // console.log(reg.test('/**/app'));
  // console.log(reg.test('app/**/app'));
  // console.log(reg.test('app/**/'));
}

{
  var isEmail = function (val) {
    var pattern = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
    var domains = ["qq.com", "163.com", "vip.163.com", "263.net", "yeah.net", "sohu.com", "sina.cn", "sina.com", "eyou.com", "gmail.com", "hotmail.com", "42du.cn"];
    if (pattern.test(val)) {
      var domain = val.substring(val.indexOf("@") + 1);
      for (var i = 0; i < domains.length; i++) {
        if (domain == domains[i]) {
          return true;
        }
      }
    }
    return false;
  }
  // 输出 true
  isEmail("cn42du@163.com");
}

{
  // 校验是否包含emoji表情
  export const isEmojiCharacter = value => {
    value = String(value);
    for (let i = 0; i < value.length; i++) {
      const hs = value.charCodeAt(i);
      if (0xd800 <= hs && hs <= 0xdbff) {
        if (value.length > 1) {
          const ls = value.charCodeAt(i + 1);
          const uc = (hs - 0xd800) * 0x400 + (ls - 0xdc00) + 0x10000;
          if (0x1d000 <= uc && uc <= 0x1f77f) {
            return true;
          }
        }
      } else if (value.length > 1) {
        const ls = value.charCodeAt(i + 1);
        if (ls == 0x20e3) {
          return true;
        }
      } else {
        if (0x2100 <= hs && hs <= 0x27ff) {
          return true;
        } else if (0x2b05 <= hs && hs <= 0x2b07) {
          return true;
        } else if (0x2934 <= hs && hs <= 0x2935) {
          return true;
        } else if (0x3297 <= hs && hs <= 0x3299) {
          return true;
        } else if (
          hs == 0xa9 ||
          hs == 0xae ||
          hs == 0x303d ||
          hs == 0x3030 ||
          hs == 0x2b55 ||
          hs == 0x2b1c ||
          hs == 0x2b1b ||
          hs == 0x2b50
        ) {
          return true;
        }
      }
    }
    return false;
  };
}

{
  // 实现返回顶部的通用方法
  function backTop(btnId) {
    var btn = document.getElementById(btnId);
    var d = document.documentElement;
    var b = document.body;
    window.onscroll = set;
    btn.style.display = 'none';
    btn.onclick = function () {
      btn.style.display = 'none';
      window.onscroll = null;
      this.timer = setInterval(function () {
        d.scrollTop -= Math.ceil((d.scrollTop + b.scrollTop) * 0.1);
        b.scrollTop -= Math.ceil((d.scrollTop + b.scrollTop) * 0.1);
        if (d.scrollTop + b.scrollTop == 0)
          clearInterval(btn.timer, (window.onscroll = set));
      }, 10);
    };
    function set() {
      btn.style.display = d.scrollTop + b.scrollTop > 100 ? 'block' : 'none';
    }
  }
  backTop('goTop');
}

{
  // 判断是否为邮箱
  function isEmail(str) {
    var re = /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/;
    if (re.test(str) != true) {
      return false;
    } else {
      return true;
    }
  }
}

{
  /**
   * 判断变量是否空值
   * undefined, null, '', false, 0, [], {} 均返回true，否则返回false
   */
  function empty(v) {
    switch (typeof v) {
      case 'undefined':
        return true;
      case 'string':
        if (trim(v).length == 0) return true;
        break;
      case 'boolean':
        if (!v) return true;
        break;
      case 'number':
        if (0 === v) return true;
        break;
      case 'object':
        if (null === v) return true;
        if (undefined !== v.length && v.length == 0) return true;
        for (var k in v) {
          return false;
        }
        return true;
        break;
    }
    return false;
  }
}

{
  // 去除对象中value为空(null,undefined,'')的属性
  export const isFalsy = (value) => (value === 0 ? false : !value);

  export const isVoid = (value) =>
    value === undefined || value === null || value === '';

  export const cleanObject = (object) => {
    // Object.assign({}, object)
    if (!object) {
      return {};
    }
    const result = { ...object };
    Object.keys(result).forEach((key) => {
      const value = result[key];
      if (isVoid(value)) {
        delete result[key];
      }
    });
    return result;
  };
}

{
  // 实现base64解码
  function base64_decode(data) {
    var b64 =
      'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
    var o1,
      o2,
      o3,
      h1,
      h2,
      h3,
      h4,
      bits,
      i = 0,
      ac = 0,
      dec = '',
      tmp_arr = [];
    if (!data) {
      return data;
    }
    data += '';
    do {
      h1 = b64.indexOf(data.charAt(i++));
      h2 = b64.indexOf(data.charAt(i++));
      h3 = b64.indexOf(data.charAt(i++));
      h4 = b64.indexOf(data.charAt(i++));
      bits = (h1 << 18) | (h2 << 12) | (h3 << 6) | h4;
      o1 = (bits >> 16) & 0xff;
      o2 = (bits >> 8) & 0xff;
      o3 = bits & 0xff;
      if (h3 == 64) {
        tmp_arr[ac++] = String.fromCharCode(o1);
      } else if (h4 == 64) {
        tmp_arr[ac++] = String.fromCharCode(o1, o2);
      } else {
        tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
      }
    } while (i < data.length);
    dec = tmp_arr.join('');
    dec = utf8_decode(dec);
    return dec;
  }
}

{
  // 实现utf8解码
  function utf8_decode(str_data) {
    var tmp_arr = [],
      i = 0,
      ac = 0,
      c1 = 0,
      c2 = 0,
      c3 = 0;
    str_data += '';
    while (i < str_data.length) {
      c1 = str_data.charCodeAt(i);
      if (c1 < 128) {
        tmp_arr[ac++] = String.fromCharCode(c1);
        i++;
      } else if (c1 > 191 && c1 < 224) {
        c2 = str_data.charCodeAt(i + 1);
        tmp_arr[ac++] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
        i += 2;
      } else {
        c2 = str_data.charCodeAt(i + 1);
        c3 = str_data.charCodeAt(i + 2);
        tmp_arr[ac++] = String.fromCharCode(
          ((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)
        );
        i += 3;
      }
    }
    return tmp_arr.join('');
  }
}

{
  // 获取窗体可见范围的宽与高
  function getViewSize() {
    var de = document.documentElement;
    var db = document.body;
    var viewW = de.clientWidth == 0 ? db.clientWidth : de.clientWidth;
    var viewH = de.clientHeight == 0 ? db.clientHeight : de.clientHeight;
    return Array(viewW, viewH);
  }
}

{
  // 小写数字转换成大写, 只处理到[0 ~ 99]
  function numberToChinese(num) {
    num = Number(num);
    let upperCaseNumber = [
      '零',
      '一',
      '二',
      '三',
      '四',
      '五',
      '六',
      '七',
      '八',
      '九',
      '十',
      '百',
      '千',
      '万',
      '亿',
    ];
    let length = String(num).length;
    if (length == 1) {
      return upperCaseNumber[num];
    } else if (length == 2) {
      if (num == 10) {
        return upperCaseNumber[num];
      } else if (num > 10 && num < 20) {
        return '十' + upperCaseNumber[String(num).charAt(1)];
      } else {
        return (
          upperCaseNumber[String(num).charAt(0)] +
          '十' +
          upperCaseNumber[String(num).charAt(1)].replace('零', '')
        );
      }
    }
  }
}

{
  // 使用场景：上传文件判断后缀名
  /**
   * 获取文件后缀名
   * @param {String} filename
   */
  export function getExt(filename) {
    if (typeof filename == 'string') {
      return filename.split('.').pop().toLowerCase();
    } else {
      throw new Error('filename must be a string type');
    }
  }
}

{
  // 复制内容到剪贴板
  export function copyToBoard(value) {
    const element = document.createElement('textarea');
    document.body.appendChild(element);
    element.value = value;
    element.select();
    if (document.execCommand('copy')) {
      document.execCommand('copy');
      document.body.removeChild(element);
      return true;
    }
    document.body.removeChild(element);
    return false;
  }
  // 如果复制成功返回true
  copyToBoard('lalallala');
}

{
  // 生成随机字符串
  /**
   * 生成随机id
   * @param {*} length
   * @param {*} chars
   */
  export function uuid(length, chars) {
    chars =
      chars || '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    length = length || 8;
    var result = '';
    for (var i = length; i > 0; --i)
      result += chars[Math.floor(Math.random() * chars.length)];
    return result;
  }
  // 使用场景：用于前端生成随机的ID,毕竟现在的Vue和React都需要绑定key
}

{
  export function uuid() {
    return crypto.randomUUID();
  }
}

{
  // 对象转化为FormData对象
  /**
   * 对象转化为formdata
   * @param {Object} object
   */
  export function getFormData(object) {
    const formData = new FormData();
    Object.keys(object).forEach((key) => {
      const value = object[key];
      if (Array.isArray(value)) {
        value.forEach((subValue, i) =>
          formData.append(key + `[${i}]`, subValue)
        );
      } else {
        formData.append(key, object[key]);
      }
    });
    return formData;
  }
  // 使用场景：上传文件时我们要新建一个FormData对象，然后有多少个参数就append多少次，使用该函数可以简化逻辑
  // 使用方式：
  let req = {
    file: xxx,
    userId: 1,
    phone: '15198763636',
    //...
  };
  fetch(getFormData(req));
}

{
  // 保留小数点以后几位，默认2位
  export function cutNumber(number, no = 2) {
    if (typeof number != 'number') {
      number = Number(number);
    }
    return Number(number.toFixed(no));
  }
}

{
  // 数字千分位分隔
  export const format = (n) => {
    let num = n.toString();
    let len = num.length;
    if (len <= 3) {
      return num;
    } else {
      let temp = '';
      let remainder = len % 3;
      if (remainder > 0) { // 不是3的整数倍
        return num.slice(0, remainder) + ',' + num.slice(remainder, len).match(/\d{3}/g).join(',') + temp;
      } else { // 3的整数倍
        return num.slice(0, len).match(/\d{3}/g).join(',') + temp;
      }
    }
  }
}

{
  // 手机号中间四位变成*
  export const telFormat = (tel) => {
    tel = String(tel);
    return tel.substr(0, 3) + "****" + tel.substr(7);
  };
}

{
  // 驼峰命名转换成短横线命名
  export const getKebabCase = (str) => {
    return str.replace(/[A-Z]/g, (item) => '-' + item.toLowerCase())
  }
  // 短横线命名转换成驼峰命名
  export const getCamelCase = (str) => {
    return str.replace(/-([a-z])/g, (i, item) => item.toUpperCase())
  }
}

{
  // 数字转化为中文数字
  export const intToChinese = value => {
    const str = String(value);
    const len = str.length - 1;
    const idxs = [
      '',
      '十',
      '百',
      '千',
      '万',
      '十',
      '百',
      '千',
      '亿',
      '十',
      '百',
      '千',
      '万',
      '十',
      '百',
      '千',
      '亿',
    ];
    const num = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
    return str.replace(/([1-9]|0+)/g, ($, $1, idx, full) => {
      let pos = 0;
      if ($1[0] !== '0') {
        pos = len - idx;
        if (idx == 0 && $1[0] == 1 && idxs[len - idx] == '十') {
          return idxs[len - idx];
        }
        return num[$1[0]] + idxs[len - idx];
      } else {
        let left = len - idx;
        let right = len - idx + $1.length;
        if (Math.floor(right / 4) - Math.floor(left / 4) > 0) {
          pos = left - (left % 4);
        }
        if (pos) {
          return idxs[pos] + num[$1[0]];
        } else if (idx + $1.length >= len) {
          return '';
        } else {
          return num[$1[0]];
        }
      }
    });
  };
}

{
  // 数字转化为大写金额
  export const digitUppercase = n => {
    const fraction = ['角', '分'];
    const digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
    const unit = [
      ['元', '万', '亿'],
      ['', '拾', '佰', '仟'],
    ];
    n = Math.abs(n);
    let s = '';
    for (let i = 0; i < fraction.length; i++) {
      s += (
        digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]
      ).replace(/零./, '');
    }
    s = s || '整';
    n = Math.floor(n);
    for (let i = 0; i < unit[0].length && n > 0; i++) {
      let p = '';
      for (let j = 0; j < unit[1].length && n > 0; j++) {
        p = digit[n % 10] + unit[1][j] + p;
        n = Math.floor(n / 10);
      }
      s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s;
    }
    return s
      .replace(/(零.)*零元/, '元')
      .replace(/(零.)+/g, '零')
      .replace(/^整$/, '零元整');
  };
}

{
  // 当前时间
  export const nowTime = () => {
    const now = new Date();
    const year = now.getFullYear();
    const month = now.getMonth();
    const date = now.getDate() >= 10 ? now.getDate() : '0' + now.getDate();
    const hour = now.getHours() >= 10 ? now.getHours() : '0' + now.getHours();
    const miu =
      now.getMinutes() >= 10 ? now.getMinutes() : '0' + now.getMinutes();
    const sec =
      now.getSeconds() >= 10 ? now.getSeconds() : '0' + now.getSeconds();
    return (
      +year +
      '年' +
      (month + 1) +
      '月' +
      date +
      '日 ' +
      hour +
      ':' +
      miu +
      ':' +
      sec
    );
  };
  // 格式化时间
  export const dateFormater = (formater, time) => {
    let date = time ? new Date(time) : new Date(),
      Y = date.getFullYear() + '',
      M = date.getMonth() + 1,
      D = date.getDate(),
      H = date.getHours(),
      m = date.getMinutes(),
      s = date.getSeconds();
    return formater
      .replace(/YYYY|yyyy/g, Y)
      .replace(/YY|yy/g, Y.substr(2, 2))
      .replace(/MM/g, (M < 10 ? '0' : '') + M)
      .replace(/DD/g, (D < 10 ? '0' : '') + D)
      .replace(/HH|hh/g, (H < 10 ? '0' : '') + H)
      .replace(/mm/g, (m < 10 ? '0' : '') + m)
      .replace(/ss/g, (s < 10 ? '0' : '') + s);
  };
  // dateFormater('YYYY-MM-DD HH:mm:ss')
  // dateFormater('YYYYMMDDHHmmss')
}

{
  // 请写一个方法，把 16 进制颜色转成 10 进制。(#fff => rgba(255,255,255))
  // 法一
  function showRGB(a) {
    if (a.substr(0, 1) == "#") {
      a = a.substring(1);
    }
    var len = a.length;
    if (len != 6 && len != 3) {
      return alert("十六进制颜色码为六位或三位！");
    } else if (/[^0-9a-f]/i.test(a)) {
      return alert("请输入正确的十六进制颜色码！");
    }
    a = a.toLowerCase();
    b = new Array();
    for (x = 0; x < 3; x++) {
      b[0] =
        len == 6 ? a.substr(x * 2, 2) : a.substr(x * 1, 1) + a.substr(x * 1, 1);
      b[3] = "0123456789abcdef";
      b[1] = b[0].substr(0, 1);
      b[2] = b[0].substr(1, 1);
      b[20 + x] = b[3].indexOf(b[1]) * 16 + b[3].indexOf(b[2]);
    }
    // console.log(b[20],b[21],b[22])
    return `rgb(${b[20]},${b[21]},${b[22]})`;
  }

  //  测试
  showRGB("#fff"); // "rgb(255,255,255)"
  showRGB("#333"); // "rgb(51,51,51)"
  showRGB("#808080"); // "rgb(128,128,128)"

  // 发二
  var colorToRgb = function (colors) {
    // 16进制颜色值的正则
    var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
    // 把颜色值变成小写
    var color = colors.toLowerCase();
    if (reg.test(color)) {
      // 如果只有三位的值，需变成六位，如：#fff => #ffffff
      if (color.length === 4) {
        var colorNew = "#";
        for (var i = 1; i < 4; i += 1) {
          colorNew += color.slice(i, i + 1).concat(color.slice(i, i + 1));
        }
        color = colorNew;
      }
      // 处理六位的颜色值，转为RGB
      var colorChange = [];
      for (var i = 1; i < 7; i += 2) {
        colorChange.push(parseInt("0x" + color.slice(i, i + 2)));
      }
      return "rgb(" + colorChange.join(",") + ")";
    } else {
      return color;
    }
  };
  console.log(colorToRgb("#fff"));
  console.log(colorToRgb("#333"));
  console.log(colorToRgb("#808080"));
}

{
  // 秒 转换为 时分秒格式，不能使用 js API
  // 超过 24 小时显示为天
  function secondToTimeStr(t) {
    if (!t) return "00:00:00";
    if (t < 60) return "00:" + ((i = t) < 10 ? "0" + i : i);
    if (t < 3600)
      return (
        "" +
        ((a = parseInt(t / 60)) < 10 ? "0" + a : a) +
        ":" +
        ((i = t % 60) < 10 ? "0" + i : i)
      );
    if (3600 <= t && t < 3600 * 24) {
      var a,
        i,
        e = parseInt(t / 3600);
      return (
        (e < 10 ? "0" + e : e) +
        ":" +
        ((a = parseInt((t % 3600) / 60)) < 10 ? "0" + a : a) +
        ":" +
        ((i = t % 60) < 10 ? "0" + i : i)
      );
    }
    if (3600 * 24 <= t) {
      let tt = parseInt(t / (3600 * 24));
      let m = t % (3600 * 24);
      return tt + "天" + secondToTimeStr(m);
    }
  }

  // 未对超过 24 小时进行处理
  function formatTime(sec) {
    return `${parseInt(sec / 3600)}:${parseInt((sec % 3600) / 60)}:${parseInt(
      sec % 60
    )}`;
  }
}

{
  // 输入一个日期,返回几秒前、几小时前、几天前、几月前
  function timeago(dateTimeStamp) {
    //dateTimeStamp是一个时间毫秒，注意时间戳是秒的形式，在这个毫秒的基础上除以1000，就是十位数的时间戳。13位数的都是时间毫秒。
    var minute = 1000 * 60; //把分，时，天，周，半个月，一个月用毫秒表示
    var hour = minute * 60;
    var day = hour * 24;
    var week = day * 7;
    var halfamonth = day * 15;
    var month = day * 30;
    var now = new Date().getTime(); //获取当前时间毫秒
    console.log(now);
    var diffValue = now - dateTimeStamp; //时间差

    if (diffValue < 0) {
      return;
    }
    var minC = diffValue / minute; //计算时间差的分，时，天，周，月
    var hourC = diffValue / hour;
    var dayC = diffValue / day;
    var weekC = diffValue / week;
    var monthC = diffValue / month;
    if (monthC >= 1 && monthC <= 3) {
      result = " " + parseInt(monthC) + "月前";
    } else if (weekC >= 1 && weekC <= 3) {
      result = " " + parseInt(weekC) + "周前";
    } else if (dayC >= 1 && dayC <= 6) {
      result = " " + parseInt(dayC) + "天前";
    } else if (hourC >= 1 && hourC <= 23) {
      result = " " + parseInt(hourC) + "小时前";
    } else if (minC >= 1 && minC <= 59) {
      result = " " + parseInt(minC) + "分钟前";
    } else if (diffValue >= 0 && diffValue <= minute) {
      result = "刚刚";
    } else {
      var datetime = new Date();
      datetime.setTime(dateTimeStamp);
      var Nyear = datetime.getFullYear();
      var Nmonth =
        datetime.getMonth() + 1 < 10
          ? "0" + (datetime.getMonth() + 1)
          : datetime.getMonth() + 1;
      var Ndate =
        datetime.getDate() < 10 ? "0" + datetime.getDate() : datetime.getDate();
      var Nhour =
        datetime.getHours() < 10
          ? "0" + datetime.getHours()
          : datetime.getHours();
      var Nminute =
        datetime.getMinutes() < 10
          ? "0" + datetime.getMinutes()
          : datetime.getMinutes();
      var Nsecond =
        datetime.getSeconds() < 10
          ? "0" + datetime.getSeconds()
          : datetime.getSeconds();
      result = Nyear + "-" + Nmonth + "-" + Ndate;
    }
    return result;
  }
}

{
  // 将 153812.7 转化为 153, 812.7

  // 法一正则版本
  function formatNumber(num) {
    /*
      ①/\B(?=(\d{3})+(?!\d))/g：正则匹配非单词边界\B，即除了1之前的位置，其他字符之间的边界，后面必须跟着3N个数字直到字符串末尾
      ②(\d{3})+：必须是1个或多个的3个连续数字;
      ③(?!\d)：第2步中的3个数字不允许后面跟着数字;
    */
    return (num + '').replace(/\B(?=(\d{3})+(?!\d))/g, ',')
  }

  // 法二 toLocaleString

  // 优点：简单粗暴，直接调用 API
  // 缺点：兼容性不太好，不过 toLocaleString ie6 都支持
  function formatNumber(num) {
    return num.toLocaleString('en-US');
  }

  // 法三 Intl对象
  // Intl 对象是 ECMAScript 国际化 API 的一个命名空间，它提供了精确的字符串对比，数字格式化，日期和时间格式化。Collator，NumberFormat 和 DateTimeFormat 对象的构造函数是 Intl 对象的属性。
  function formatNumber(num) {
    return new Intl.NumberFormat().format(num);
  }

  // 法四
  function formatNumber(num) {
    //str.split('').reverse() => ["0", "9", "8", "7", "6", "5", "4", "3", "2", "1"]
    return num.toString().split('').reverse().reduce((prev, next, index) => {
      return ((index % 3) ? next : (next + ',')) + prev
    })
  }
  console.log(formatNumber(153812.7));
}

{
  // 解析url参数
  function parseQueryString(url) {
    if (typeof url !== 'string') throw new Error('invalid url')
    const search = decodeURIComponent(url).split('?')[1];
    if (!search) return {};
    return search.split('&').reduce((pre, cur) => {
      const [key, value] = cur.split('=');
      pre[key] = value;
      return pre;
    }, {});
  }

  const getUrlParams = (url = window.location.search) => {
    const urlSearchParams = new URLSearchParams(url);
    const params = Object.fromEntries(urlSearchParams.entries()) ?? {};
    return params;
  };
  parseQueryString("http://iauto360.cn/index.php?key0=0&key1=1&key2=2")
}

{
  // 写一个方法，把 16 进制颜色转成 10 进制。(#fff => rgba(255,255,255))

  // 法一
  function showRGB(a) {
    if (a.substr(0, 1) == "#") {
      a = a.substring(1);
    }
    var len = a.length;
    if (len != 6 && len != 3) {
      return alert("十六进制颜色码为六位或三位！");
    } else if (/[^0-9a-f]/i.test(a)) {
      return alert("请输入正确的十六进制颜色码！");
    }
    a = a.toLowerCase();
    b = new Array();
    for (x = 0; x < 3; x++) {
      b[0] =
        len == 6 ? a.substr(x * 2, 2) : a.substr(x * 1, 1) + a.substr(x * 1, 1);
      b[3] = "0123456789abcdef";
      b[1] = b[0].substr(0, 1);
      b[2] = b[0].substr(1, 1);
      b[20 + x] = b[3].indexOf(b[1]) * 16 + b[3].indexOf(b[2]);
    }
    // console.log(b[20],b[21],b[22])
    return `rgb(${b[20]},${b[21]},${b[22]})`;
  }

  //  测试
  showRGB("#fff"); // "rgb(255,255,255)"
  showRGB("#333"); // "rgb(51,51,51)"
  showRGB("#808080"); // "rgb(128,128,128)"

  // 法二
  var colorToRgb = function (colors) {
    // 16进制颜色值的正则
    var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
    // 把颜色值变成小写
    var color = colors.toLowerCase();
    if (reg.test(color)) {
      // 如果只有三位的值，需变成六位，如：#fff => #ffffff
      if (color.length === 4) {
        var colorNew = "#";
        for (var i = 1; i < 4; i += 1) {
          colorNew += color.slice(i, i + 1).concat(color.slice(i, i + 1));
        }
        color = colorNew;
      }
      // 处理六位的颜色值，转为RGB
      var colorChange = [];
      for (var i = 1; i < 7; i += 2) {
        colorChange.push(parseInt("0x" + color.slice(i, i + 2)));
      }
      return "rgb(" + colorChange.join(",") + ")";
    } else {
      return color;
    }
  };
  console.log(colorToRgb("#fff"));
  console.log(colorToRgb("#333"));
  console.log(colorToRgb("#808080"));
}


{
  // 转化为驼峰命名
  var s1 = "get-element-by-id"
  // 转化为 getElementById

  var f = function (str) {
    // 匹配 - 后面的第一个字符串，并转化成大写替换掉
    // \w 匹配a到z，0到9，以及下划线_ 
    return str.replace(/-\w/g, function (x) {
      return x.slice(1).toUpperCase()
    })
  }
  // console.log(f(s1))

  let str = 'require("a.js")'
  let replaceRequire = function (str) {
    // 分组匹配 1 require 一组 2 (" 一组 3 a.js 一组 4 ") 一组
    // (.+?) 匹配连续的字符
    return str.replace(/(require)\(['"](.+?)['"]\)/g, ($1, $2, $3, $4) => {
      console.log($1)
      console.log($2)
      console.log($3)
      console.log($4)
      return $2 = `__webpack_require__(${$3})`
    })
  }
  // console.log(replaceRequire(str))
}

{
  // 手写代码实现kuai-shou-front-end => KuaiShouFrontEnd（转化驼峰）
  // 法一 map
  function firstUp(str) {
    return str
      .split('-')
      .map((item) => {
        return item[0].toUpperCase() + item.slice(1);
      })
      .join('');
  }

  // 法二 正则

  function firstUp(str) {
    return str.replace(/-[a-z]/g, function (match) {
      return match.replace('-', '').toUpperCase();
    });
  }
  console.log(firstUp('kuai-shou-front-end'));
}

{
  // 判断一个值是否是promise like
  function isPromiseLike(value) {
    return (
      value !== null &&
      (typeof value === 'object' || typeof value === 'function') &&
      typeof value.then === 'function'
    );
  }
}